home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / pwio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-26  |  11.0 KB  |  589 lines

  1. /*
  2.  * Copyright 1990, 1991, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  *
  11.  *    This file implements a transaction oriented password database
  12.  *    library.  The password file is updated one entry at a time.
  13.  *    After each transaction the file must be logically closed and
  14.  *    transferred to the existing password file.  The sequence of
  15.  *    events is
  16.  *
  17.  *    pw_lock                -- lock password file
  18.  *    pw_open                -- logically open password file
  19.  *    while transaction to process
  20.  *        pw_(locate,update,remove) -- perform transaction
  21.  *    done
  22.  *    pw_close            -- commit transactions
  23.  *    pw_unlock            -- remove password lock
  24.  */
  25.  
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <fcntl.h>
  29. #include <errno.h>
  30. #include "pwd.h"
  31. #include <stdio.h>
  32.  
  33. #ifdef    BSD
  34. # include <strings.h>
  35. #else
  36. # include <string.h>
  37. #endif
  38.  
  39. #ifndef lint
  40. static    char    sccsid[] = "@(#)pwio.c    3.9    08:46:13    9/12/91";
  41. #endif
  42.  
  43. static    int    islocked;
  44. static    int    isopen;
  45. static    int    open_modes;
  46. static    FILE    *pwfp;
  47.  
  48. struct    pw_file_entry {
  49.     char    *pwf_line;
  50.     int    pwf_changed;
  51.     struct    passwd    *pwf_entry;
  52.     struct    pw_file_entry *pwf_next;
  53. };
  54.  
  55. static    struct    pw_file_entry    *pwf_head;
  56. static    struct    pw_file_entry    *pwf_tail;
  57. static    struct    pw_file_entry    *pwf_cursor;
  58. static    int    pw_changed;
  59. static    int    lock_pid;
  60.  
  61. #define    PW_LOCK    "/etc/passwd.lock"
  62. #define    PW_TEMP "/etc/pwd.%d"
  63. #define    PASSWD    "/etc/passwd"
  64.  
  65. static    char    pw_filename[BUFSIZ] = PASSWD;
  66.  
  67. extern    int    fputs();
  68. extern    char    *fgets();
  69. extern    char    *strdup();
  70. extern    char    *malloc();
  71. extern    struct    passwd    *sgetpwent();
  72.  
  73. /*
  74.  * pw_dup - duplicate a password file entry
  75.  *
  76.  *    pw_dup() accepts a pointer to a password file entry and
  77.  *    returns a pointer to a password file entry in allocated
  78.  *    memory.
  79.  */
  80.  
  81. static struct passwd *
  82. pw_dup (pwent)
  83. struct    passwd    *pwent;
  84. {
  85.     struct    passwd    *pw;
  86.  
  87.     if (! (pw = (struct passwd *) malloc (sizeof *pw)))
  88.         return 0;
  89.  
  90.     if ((pw->pw_name = strdup (pwent->pw_name)) == 0 ||
  91.             (pw->pw_passwd = strdup (pwent->pw_passwd)) == 0 ||
  92. #ifdef    ATT_AGE
  93.             (pw->pw_age = strdup (pwent->pw_age)) == 0 ||
  94. #endif    /* ATT_AGE */
  95. #ifdef    ATT_COMMENT
  96.             (pw->pw_comment = strdup (pwent->pw_comment)) == 0 ||
  97. #endif    /* ATT_COMMENT */
  98.             (pw->pw_gecos = strdup (pwent->pw_gecos)) == 0 ||
  99.             (pw->pw_dir = strdup (pwent->pw_dir)) == 0 ||
  100.             (pw->pw_shell = strdup (pwent->pw_shell)) == 0)
  101.         return 0;
  102.  
  103.     pw->pw_uid = pwent->pw_uid;
  104.     pw->pw_gid = pwent->pw_gid;
  105.  
  106.     return pw;
  107. }
  108.  
  109. /*
  110.  * pw_free - free a dynamically allocated password file entry
  111.  *
  112.  *    pw_free() frees up the memory which was allocated for the
  113.  *    pointed to entry.
  114.  */
  115.  
  116. static void
  117. pw_free (pwent)
  118. struct    passwd    *pwent;
  119. {
  120.     free (pwent->pw_name);
  121.     free (pwent->pw_passwd);
  122.     free (pwent->pw_gecos);
  123.     free (pwent->pw_dir);
  124.     free (pwent->pw_shell);
  125. }
  126.  
  127. /*
  128.  * pw_name - change the name of the password file
  129.  */
  130.  
  131. int
  132. pw_name (name)
  133. char    *name;
  134. {
  135.     if (isopen || strlen (name) > (BUFSIZ-10))
  136.         return -1;
  137.  
  138.     strcpy (pw_filename, name);
  139.     return 0;
  140. }
  141.  
  142. /*
  143.  * pw_lock - lock a password file
  144.  *
  145.  *    pw_lock() encapsulates the lock operation.  it returns
  146.  *    TRUE or FALSE depending on the password file being
  147.  *    properly locked.  the lock is set by creating a semaphore
  148.  *    file, PW_LOCK.
  149.  */
  150.  
  151. int
  152. pw_lock ()
  153. {
  154.     int    fd;
  155.     int    pid;
  156.     int    len;
  157.     char    file[BUFSIZ];
  158.     char    buf[32];
  159.     struct    stat    sb;
  160.  
  161.     if (islocked)
  162.         return 1;
  163.  
  164.     if (strcmp (pw_filename, PASSWD) != 0)
  165.         return 0;
  166.  
  167.     /*
  168.      * Create a lock file which can be switched into place
  169.      */
  170.  
  171.     sprintf (file, PW_TEMP, lock_pid = getpid ());
  172.     if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
  173.         return 0;
  174.  
  175.     sprintf (buf, "%d", lock_pid);
  176.     if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
  177.         (void) close (fd);
  178.         (void) unlink (file);
  179.         return 0;
  180.     }
  181.     close (fd);
  182.  
  183.     /*
  184.      * Simple case first -
  185.      *    Link fails (in a sane environment ...) if the target
  186.      *    exists already.  So we try to switch in a new lock
  187.      *    file.  If that succeeds, we assume we have the only
  188.      *    valid lock.  Needs work for NFS where this assumption
  189.      *    may not hold.  The simple hack is to check the link
  190.      *    count on the source file, which should be 2 iff the
  191.      *    link =really= worked.
  192.      */
  193.  
  194.     if (link (file, PW_LOCK) == 0) {
  195.         if (stat (file, &sb) != 0)
  196.             return 0;
  197.  
  198.         if (sb.st_nlink != 2)
  199.             return 0;
  200.  
  201.         (void) unlink (file);
  202.         islocked = 1;
  203.         return 1;
  204.     }
  205.  
  206.     /*
  207.      * Invalid lock test -
  208.      *    Open the lock file and see if the lock is valid.
  209.      *    The PID of the lock file is checked, and if the PID
  210.      *    is not valid, the lock file is removed.  If the unlink
  211.      *    of the lock file fails, it should mean that someone
  212.      *    else is executing this code.  They will get success,
  213.      *    and we will fail.
  214.      */
  215.  
  216.     if ((fd = open (PW_LOCK, O_RDWR)) == -1 ||
  217.             (len = read (fd, buf, BUFSIZ)) <= 0) {
  218.         errno = EINVAL;
  219.         return 0;
  220.     }
  221.     buf[len] = '\0';
  222.     if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
  223.         errno = EINVAL;
  224.         return 0;
  225.     }
  226.     if (kill (pid, 0) == 0)  {
  227.         errno = EEXIST;
  228.         return 0;
  229.     }
  230.     if (unlink (PW_LOCK)) {
  231.         (void) close (fd);
  232.         (void) unlink (file);
  233.  
  234.         return 0;
  235.     }
  236.  
  237.     /*
  238.      * Re-try lock -
  239.      *    The invalid lock has now been removed and I should
  240.      *    be able to acquire a lock for myself just fine.  If
  241.      *    this fails there will be no retry.  The link count
  242.      *    test here makes certain someone executing the previous
  243.      *    block of code didn't just remove the lock we just
  244.      *    linked to.
  245.      */
  246.  
  247.     if (link (file, PW_LOCK) == 0) {
  248.         if (stat (file, &sb) != 0)
  249.             return 0;
  250.  
  251.         if (sb.st_nlink != 2)
  252.             return 0;
  253.  
  254.         (void) unlink (file);
  255.         islocked = 1;
  256.         return 1;
  257.     }
  258.     (void) unlink (file);
  259.     return 0;
  260. }
  261.  
  262. /*
  263.  * pw_unlock - logically unlock a password file
  264.  *
  265.  *    pw_unlock() removes the lock which was set by an earlier
  266.  *    invocation of pw_lock().
  267.  */
  268.  
  269. int
  270. pw_unlock ()
  271. {
  272.     if (isopen) {
  273.         open_modes = O_RDONLY;
  274.         if (! pw_close ())
  275.             return 0;
  276.     }
  277.       if (islocked) {
  278.           islocked = 0;
  279.         if (lock_pid != getpid ())
  280.             return 0;
  281.  
  282.         (void) unlink (PW_LOCK);
  283.           return 1;
  284.     }
  285.     return 0;
  286. }
  287.  
  288. /*
  289.  * pw_open - open a password file
  290.  *
  291.  *    pw_open() encapsulates the open operation.  it returns
  292.  *    TRUE or FALSE depending on the password file being
  293.  *    properly opened.
  294.  */
  295.  
  296. int
  297. pw_open (mode)
  298. int    mode;
  299. {
  300.     char    buf[8192];
  301.     char    *cp;
  302.     struct    pw_file_entry    *pwf;
  303.     struct    passwd    *pwent;
  304.  
  305.     if (isopen || (mode != O_RDONLY && mode != O_RDWR))
  306.         return 0;
  307.  
  308.     if (mode != O_RDONLY && ! islocked &&
  309.             strcmp (pw_filename, PASSWD) == 0)
  310.         return 0;
  311.  
  312.     if ((pwfp = fopen (pw_filename, mode == O_RDONLY ? "r":"r+")) == 0)
  313.         return 0;
  314.  
  315.     pwf_head = pwf_tail = pwf_cursor = 0;
  316.     pw_changed = 0;
  317.  
  318.     while (fgets (buf, sizeof buf, pwfp) != (char *) 0) {
  319.         if (cp = strrchr (buf, '\n'))
  320.             *cp = '\0';
  321.  
  322.         if (! (pwf = (struct pw_file_entry *) malloc (sizeof *pwf)))
  323.             return 0;
  324.  
  325.         pwf->pwf_changed = 0;
  326.         pwf->pwf_line = strdup (buf);
  327.         if ((pwent = sgetpwent (buf)) && ! (pwent = pw_dup (pwent)))
  328.             return 0;
  329.  
  330.         pwf->pwf_entry = pwent;
  331.  
  332.         if (pwf_head == 0) {
  333.             pwf_head = pwf_tail = pwf;
  334.             pwf->pwf_next = 0;
  335.         } else {
  336.             pwf_tail->pwf_next = pwf;
  337.             pwf->pwf_next = 0;
  338.             pwf_tail = pwf;
  339.         }
  340.     }
  341.     isopen++;
  342.     open_modes = mode;
  343.  
  344.     return 1;
  345. }
  346.  
  347. /*
  348.  * pw_close - close the password file
  349.  *
  350.  *    pw_close() outputs any modified password file entries and
  351.  *    frees any allocated memory.
  352.  */
  353.  
  354. int
  355. pw_close ()
  356. {
  357.     char    backup[BUFSIZ];
  358.     int    mask;
  359.     int    c;
  360.     int    errors = 0;
  361.     FILE    *bkfp;
  362.     struct    pw_file_entry *pwf;
  363.     struct    stat    sb;
  364.  
  365.     if (! isopen) {
  366.         errno = EINVAL;
  367.         return 0;
  368.     }
  369.     if (islocked && lock_pid != getpid ()) {
  370.         isopen = 0;
  371.         islocked = 0;
  372.         errno = EACCES;
  373.         return 0;
  374.     }
  375.     strcpy (backup, pw_filename);
  376.     strcat (backup, "-");
  377.  
  378.     if (open_modes == O_RDWR && pw_changed) {
  379.         mask = umask (0222);
  380.         if ((bkfp = fopen (backup, "w")) == 0) {
  381.             umask (mask);
  382.             return 0;
  383.         }
  384.         umask (mask);
  385.         fstat (fileno (pwfp), &sb);
  386.         chown (backup, sb.st_uid, sb.st_gid);
  387.  
  388.         rewind (pwfp);
  389.         while ((c = getc (pwfp)) != EOF) {
  390.             if (putc (c, bkfp) == EOF) {
  391.                 fclose (bkfp);
  392.                 return 0;
  393.             }
  394.         }
  395.         if (fclose (bkfp))
  396.             return 0;
  397.  
  398.         isopen = 0;
  399.         (void) fclose (pwfp);
  400.  
  401.         mask = umask (0222);
  402.         if (! (pwfp = fopen (pw_filename, "w"))) {
  403.             umask (mask);
  404.             return 0;
  405.         }
  406.         umask (mask);
  407.  
  408.         for (pwf = pwf_head;errors == 0 && pwf;pwf = pwf->pwf_next) {
  409.             if (pwf->pwf_changed) {
  410.                 if (putpwent (pwf->pwf_entry, pwfp))
  411.                     errors++;
  412.             } else {
  413.                 if (fputs (pwf->pwf_line, pwfp) == EOF)
  414.                     errors++;
  415.                 if (putc ('\n', pwfp) == EOF)
  416.                     errors++;
  417.             }
  418.         }
  419.         if (fflush (pwfp))
  420.             errors++;
  421.  
  422.         if (errors) {
  423.             unlink (pw_filename);
  424.             link (backup, pw_filename);
  425.             unlink (backup);
  426.             return 0;
  427.         }
  428.     }
  429.     if (fclose (pwfp))
  430.         return 0;
  431.  
  432.     pwfp = 0;
  433.  
  434.     while (pwf_head != 0) {
  435.         pwf = pwf_head;
  436.         pwf_head = pwf->pwf_next;
  437.  
  438.         if (pwf->pwf_entry) {
  439.             pw_free (pwf->pwf_entry);
  440.             free (pwf->pwf_entry);
  441.         }
  442.         if (pwf->pwf_line)
  443.             free (pwf->pwf_line);
  444.  
  445.         free (pwf);
  446.     }
  447.     pwf_tail = 0;
  448.     isopen = 0;
  449.     return 1;
  450. }
  451.  
  452. int
  453. pw_update (pwent)
  454. struct    passwd    *pwent;
  455. {
  456.     struct    pw_file_entry    *pwf;
  457.     struct    passwd    *npw;
  458.  
  459.     if (! isopen || open_modes == O_RDONLY) {
  460.         errno = EINVAL;
  461.         return 0;
  462.     }
  463.     for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
  464.         if (pwf->pwf_entry == 0)
  465.             continue;
  466.  
  467.         if (strcmp (pwent->pw_name, pwf->pwf_entry->pw_name) != 0)
  468.             continue;
  469.  
  470.         if (! (npw = pw_dup (pwent)))
  471.             return 0;
  472.         else {
  473.             pw_free (pwf->pwf_entry);
  474.             *(pwf->pwf_entry) = *npw;
  475.         }
  476.         pwf->pwf_changed = 1;
  477.         pwf_cursor = pwf;
  478.         return pw_changed = 1;
  479.     }
  480.     pwf = (struct pw_file_entry *) malloc (sizeof *pwf);
  481.     if (! (pwf->pwf_entry = pw_dup (pwent)))
  482.         return 0;
  483.  
  484.     pwf->pwf_changed = 1;
  485.     pwf->pwf_next = 0;
  486.     pwf->pwf_line = 0;
  487.  
  488.     if (pwf_tail)
  489.         pwf_tail->pwf_next = pwf;
  490.  
  491.     if (! pwf_head)
  492.         pwf_head = pwf;
  493.  
  494.     pwf_tail = pwf;
  495.  
  496.     return pw_changed = 1;
  497. }
  498.  
  499. int
  500. pw_remove (name)
  501. char    *name;
  502. {
  503.     struct    pw_file_entry    *pwf;
  504.     struct    pw_file_entry    *opwf;
  505.  
  506.     if (! isopen || open_modes == O_RDONLY) {
  507.         errno = EINVAL;
  508.         return 0;
  509.     }
  510.     for (opwf = 0, pwf = pwf_head;pwf != 0;
  511.             opwf = pwf, pwf = pwf->pwf_next) {
  512.         if (! pwf->pwf_entry)
  513.             continue;
  514.  
  515.         if (strcmp (name, pwf->pwf_entry->pw_name) != 0)
  516.             continue;
  517.  
  518.         if (pwf == pwf_cursor)
  519.             pwf_cursor = opwf;
  520.  
  521.         if (opwf != 0)
  522.             opwf->pwf_next = pwf->pwf_next;
  523.         else
  524.             pwf_head = pwf->pwf_next;
  525.  
  526.         if (pwf == pwf_tail)
  527.             pwf_tail = opwf;
  528.  
  529.         return pw_changed = 1;
  530.     }
  531.     errno = ENOENT;
  532.     return 0;
  533. }
  534.  
  535. struct passwd *
  536. pw_locate (name)
  537. char    *name;
  538. {
  539.     struct    pw_file_entry    *pwf;
  540.  
  541.     if (! isopen) {
  542.         errno = EINVAL;
  543.         return 0;
  544.     }
  545.     for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
  546.         if (pwf->pwf_entry == 0)
  547.             continue;
  548.  
  549.         if (strcmp (name, pwf->pwf_entry->pw_name) == 0) {
  550.             pwf_cursor = pwf;
  551.             return pwf->pwf_entry;
  552.         }
  553.     }
  554.     errno = ENOENT;
  555.     return 0;
  556. }
  557.  
  558. int
  559. pw_rewind ()
  560. {
  561.     if (! isopen) {
  562.         errno = EINVAL;
  563.         return 0;
  564.     }
  565.     pwf_cursor = 0;
  566.     return 1;
  567. }
  568.  
  569. struct passwd *
  570. pw_next ()
  571. {
  572.     if (! isopen) {
  573.         errno = EINVAL;
  574.         return 0;
  575.     }
  576.     if (pwf_cursor == 0)
  577.         pwf_cursor = pwf_head;
  578.     else
  579.         pwf_cursor = pwf_cursor->pwf_next;
  580.  
  581.     while (pwf_cursor) {
  582.         if (pwf_cursor->pwf_entry)
  583.             return pwf_cursor->pwf_entry;
  584.  
  585.         pwf_cursor = pwf_cursor->pwf_next;
  586.     }
  587.     return 0;
  588. }
  589.